package org.zyj.io.time;

import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;

import java.util.Date;

//解决半包问题
public class TimeClient2 {

    public static void main(String[] args) throws Exception {
        String host = "localhost";
        int port = TimeServer.SERVER_PORT;

        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            //客户端侧使用Bootstrap来创建连接
            Bootstrap b = new Bootstrap();
            //只指定一个EventLoopGroup, Boss和Worker的职责都会承担
            b.group(workerGroup);
            //客户端侧用NioSocketChannel来创建连接
            b.channel(NioSocketChannel.class);
            //客户端没有childOption()
            b.option(ChannelOption.SO_KEEPALIVE, true); // (4)
            b.handler(new ChannelInitializer<SocketChannel>() {
                @Override
                public void initChannel(SocketChannel ch) throws Exception {
                    ch.pipeline().addLast(new TimeClientHandler());
                }
            });

            //客户端发起连接, 使用connect()方法
            ChannelFuture f = b.connect(host, port).sync(); // (5)

            // Wait until the connection is closed.
            f.channel().closeFuture().sync();
        } finally {
            workerGroup.shutdownGracefully();
        }
    }

    /**
     * 虽然服务端一次只发送4字节的数据
     * 但是没有保证绝对每次从缓存中会读取的就是4字节数据
     * 这里如果我们知道数据的大小, 我们可以直接规定buffer接收到这么多数据之后再处理
     */
    public static class TimeClientHandler extends ChannelInboundHandlerAdapter {
        private ByteBuf buf;

        /**
         * Gets called after the {@link ChannelHandler} was added to the actual context and it's ready to handle events.
         * 当ChannelHandler被添加进pipeline的时候会被调用
         */
        @Override
        public void handlerAdded(ChannelHandlerContext ctx) {
            //可以进行一些初始化操作, 操作少一些, 尽量不要是耗时操作
            buf = ctx.alloc().buffer(4); // 这里是初始化了ByteBuf,设置大小为4字节
        }

        /**
         * Gets called after the {@link ChannelHandler} was removed from the actual context and it doesn't handle events anymore.
         */
        @Override
        public void handlerRemoved(ChannelHandlerContext ctx) {
            //析构函数
            buf.release(); //释放这个ByteBuf的内存
            buf = null;
        }

        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) {
            ByteBuf m = (ByteBuf) msg;
            //将socket缓冲区的内容, 放入我们自己初始化的缓存中 buf
            buf.writeBytes(m);
            m.release();//释放socket缓冲区

            // 如果buf接受到的数据不足4字节, 说明没接收完全, 需要等待下次TCP数据进来,
            // 直到buf中的内容达到4个字节
            if (buf.readableBytes() >= 4) {
                long currentTimeMillis = (buf.readUnsignedInt() - 2208988800L) * 1000L;
                System.out.println(new Date(currentTimeMillis));
                ctx.close();
            }
        }

        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
            cause.printStackTrace();
            ctx.close();
        }

    }
}
